/**
 *   DataSlim - Lazy Loading Framework
 *   Copyright (C) 2007 Rodolfo Dias
 *   
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this program; if not, write to the Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 
 */

package net.dataslim.processor;

import java.io.Serializable;
import java.lang.reflect.Method;

import javax.naming.Context;
import javax.naming.InitialContext;

import net.dataslim.annotations.DataSlimFind;
import net.dataslim.annotations.DataSlimLazy;

public class DataSlimProxied implements Serializable {

	Object oDataSlim = null;
	public DataSlimProxied(Object oDataSlim){
		this.oDataSlim = oDataSlim;
	}
	
	public Object getProxiedValue(String nameMethod){
		Class c = oDataSlim.getClass().getSuperclass();
		Method[] methods = c.getMethods();
		for(Method m : methods ){
			if (m.getName().equals(nameMethod)){ 
				DataSlimLazy lazyRemote = m.getAnnotation(DataSlimLazy.class);
				if (lazyRemote != null){
					try{
						Object objectWithLazyDataSlimFind = getObjectWithDataSlimFind(lazyRemote);
						if (objectWithLazyDataSlimFind != null){
							Method objectWithLazyFindMethod = getMethodWithLazyFind(lazyRemote, objectWithLazyDataSlimFind);
							System.out.println("Executando o metodo proxiado:" +  
									objectWithLazyFindMethod.getName() + " com " + 
									(lazyRemote.parameters().length) + " parametro(s).");
							return objectWithLazyFindMethod.invoke( 
									objectWithLazyDataSlimFind, 
									toObject(lazyRemote.parameters()) );
						}
					}catch(Exception ee){
						ee.printStackTrace();
					}
				}
			}
		}
		return null;
	}
	
	private Method getMethodWithLazyFind(DataSlimLazy lazyRemote, Object objectWithLazyDataSlimFind) throws Exception{
		Method objectWithLazyFindMethod = null;
		Class[] arrClazz = objectWithLazyDataSlimFind.getClass().getInterfaces();
		for(int i = 0, s = arrClazz.length; i < s && objectWithLazyFindMethod == null; i++) {
			Method[] methodsClazz = arrClazz[i].getMethods();
			for(Method methodClazz : methodsClazz){
			    DataSlimFind dataSlimFind = methodClazz.getAnnotation(DataSlimFind.class);
				if (dataSlimFind != null && dataSlimFind.key().equals(lazyRemote.mappedBy())){
					objectWithLazyFindMethod = objectWithLazyDataSlimFind.getClass().getMethod( methodClazz.getName(), (Class[])methodClazz.getParameterTypes() );
					break;
				}
			}
		}
		return objectWithLazyFindMethod;
	}
	
	private Object getObjectWithDataSlimFind(DataSlimLazy lazyRemote) throws Exception{
		Object objectWithLazyDataSlimFind = null;
		if (!"".equals(lazyRemote.className())){
			Object classObject = Class.forName(lazyRemote.className(), true, Thread.currentThread().getContextClassLoader()).newInstance();
			if (lazyRemote.factoryMethod().equals("")){
				objectWithLazyDataSlimFind = classObject;
			}else{
				Method factoryMethod = classObject.getClass().getMethod(lazyRemote.factoryMethod(), new Class[]{});
				objectWithLazyDataSlimFind = factoryMethod.invoke(classObject, new Object[]{});
			}
		}else if(!lazyRemote.jndiName().equals("")){
			try	{
				Context ct = new InitialContext();
				objectWithLazyDataSlimFind = ct.lookup(lazyRemote.jndiName());
			}catch (Exception e){
				e.printStackTrace();
			}   
		}
		return objectWithLazyDataSlimFind;
	}
	
		
	private Object[] toObject(String[] parameters) throws Exception{
	    Object[] oParameters = new Object[parameters.length];
		int cont = 0;
		for(String parameter : parameters){
		    int isValorConstante = -1;
		    if ( (isValorConstante = parameter.indexOf("CONST_")) != -1){
		        oParameters[cont++] = parameter.substring(isValorConstante);
		        System.out.println("Encontrado uma constante no "  + cont + "parametro de valor : " + oParameters[cont]);
		    }else{
				oParameters[cont++] = getParameter(parameter);
			}
		}
		return oParameters;
	}
	
	private Object getParameter(String parameter) throws Exception{
		Class c = oDataSlim.getClass();
		String nameMethod = "get"+parameter.substring(0,1).toUpperCase() + parameter.substring(1);
		System.out.println("Metodo encontrado: " + nameMethod);
		return c.getMethod(nameMethod, new Class[]{}).invoke(oDataSlim, new Object[]{});
	}
		
	
}
